package cn.stylefeng.guns.modular.trc.service;

import cn.hutool.core.util.StrUtil;
import cn.stylefeng.guns.modular.app.entity.MemberRechargeAddress;
import cn.stylefeng.guns.modular.app.service.MemberRechargeAddressService;
import cn.stylefeng.guns.modular.chain.entity.Recharge;
import cn.stylefeng.guns.modular.chain.service.RechargeService;
import cn.stylefeng.guns.modular.job.ScanBlocksJob;
import cn.stylefeng.guns.modular.trc.config.TrcConfig;
import cn.stylefeng.guns.modular.trc.config.TrcProperty;
import cn.stylefeng.guns.modular.trc.vo.TrcKeyVO;
import com.google.protobuf.ByteString;
import com.google.protobuf.Message;
import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.jcajce.provider.digest.Keccak;
import org.bouncycastle.util.encoders.Hex;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.tron.trident.abi.TypeDecoder;
import org.tron.trident.abi.datatypes.Address;
import org.tron.trident.abi.datatypes.NumericType;
import org.tron.trident.core.ApiWrapper;
import org.tron.trident.core.contract.Contract;
import org.tron.trident.core.contract.Trc20Contract;
import org.tron.trident.core.exceptions.IllegalException;
import org.tron.trident.core.key.KeyPair;
import org.tron.trident.crypto.Hash;
import org.tron.trident.proto.Chain;
import org.tron.trident.proto.Response;
import org.tron.trident.utils.Base58Check;
import org.tron.trident.utils.Convert;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;
import java.util.List;


@Slf4j
@Service
public class TrcRechargeService {


    @Autowired
    private TrcProperty trcProperty;

    @Autowired
    private TrcConfig trcConfig;


    @Autowired
    private RechargeService rechargeService;


    @Autowired
    private MemberRechargeAddressService memberRechargeAddressService;


    public TrcKeyVO createKey() {
        KeyPair keyPair = KeyPair.generate();
        TrcKeyVO vo = new TrcKeyVO();
        vo.setPrivateKey(keyPair.toPrivateKey());
        vo.setAddress(keyPair.toBase58CheckAddress());
        vo.setPublicKey(keyPair.toPublicKey());
        return vo;
    }


    /**
     * TRC20 转账
     *
     * @param fromAddress
     * @param amount
     * @return
     */
    public String transTrc20(String fromAddress,String fromPrivateKey,  BigDecimal amount) {

        String toAddress = trcProperty.getImputationAddress();
//        String payAddress = trcProperty.getPayAddress();
        ApiWrapper fromWrapper = trcConfig.getFromWrapper(fromPrivateKey);

        BigDecimal tRxBalance = getTRxBalance(fromAddress);
        BigDecimal need  = BigDecimal.valueOf(30);
        if(tRxBalance.compareTo(need) <0){
            String s = transferTRX(fromAddress, need.subtract(tRxBalance).floatValue());
            log.info("转TRX返回" + s);
            return "error_trx";
        }

        ApiWrapper apiWrapper = trcConfig.getApiWrapper();

        try {
            Contract contract = trcConfig.getContract(apiWrapper);
            Trc20Contract fromToken = trcConfig.getUserToken(fromWrapper, contract,fromAddress);
            // Trc20Contract payToken = trcConfig.getPayToken(apiWrapper, contract);

            String transfer ;
            // 获取转账账户的余额
            BigInteger trc20Value = fromToken.balanceOf(fromAddress);
            // 获取想要转账的数额
            BigInteger sunAmountValue = amount.multiply(BigDecimal.valueOf(Math.pow(10,trcProperty.getDivisor()))).toBigInteger();
            // 进行比较
            if (trc20Value.compareTo(sunAmountValue) >= 0) {

                log.info("开始转账.........");
                // 设置最大矿工费用
                long feeLimit = Convert.toSun(need, Convert.Unit.TRX).longValue();
                transfer = fromToken.transfer(toAddress, amount.longValue(), trcProperty.getDivisor(), "", feeLimit);
                log.info("转账。。。" + transfer);

            } else {
                return "error_error";
            }
            if (StrUtil.isEmpty(transfer)) {
                return "error_error";
            }
            log.info("交易ID:{}", transfer);
            return "ok";
        } catch (Exception ex) {
            String message = ex.getMessage();
            log.error(fromAddress+"错误:"+message+"；金额"+amount);
            return "error_error";
        } finally {
        //    apiWrapper.close();
            fromWrapper.close();
        }
    }


    /**
     * TRx余额
     *
     * @param address
     * @return
     */
    public BigDecimal getTRxBalance(String address) {
        ApiWrapper wrapper = trcConfig.getApiWrapper();
        Long balance = wrapper.getAccountBalance(address);
        BigDecimal divisor = BigDecimal.valueOf(Math.pow(10, 6));
        BigDecimal divide = new BigDecimal(balance).divide(divisor, 4, RoundingMode.HALF_UP);
        return divide;
    }


    /**
     * TRX转账
     *
     * @param toAddress 转账地址
     * @param amount    金额
     * @return
     */
    public String transferTRX(String toAddress, float amount) {
        ApiWrapper wrapper = trcConfig.getApiWrapper();
        try {
            String fromAddress = trcProperty.getPayAddress();
            BigDecimal divisor = BigDecimal.valueOf(Math.pow(10, 6));
            Long rechangeAmount = new BigDecimal(String.valueOf(amount)).multiply(divisor).longValue();
            // 创建交易
            Response.TransactionExtention transaction = wrapper.transfer(fromAddress, toAddress, rechangeAmount);
        //  log.info("transaction:{}", transaction);
        //  log.info("transaction.Txid:{}", transaction.getTxid());
            // 签名交易
            Chain.Transaction signTransaction = wrapper.signTransaction(transaction);
        //  log.info("signTransaction:{}", signTransaction);
            // 计算交易所需要的宽带
       //  long byteSize = wrapper.estimateBandwidth(signTransaction);
      //   log.info("byteSize:{}", byteSize);
            // 广播交易
            String hashTx = wrapper.broadcastTransaction(signTransaction);
        //    log.info("hashTRX:{}", hashTx);
            return hashTx;
        } catch (Exception e) {
            log.error("TransactionService#transfer error: {}", e.getMessage());
        }
//        finally {
//            wrapper.close();
//        }
        return null;
    }


    /**
     * 检索块信息，获取交易信息
     *
     * @param blockExtention
     * @throws Throwable
     */
    public void switchDataTransaction(Response.BlockExtention blockExtention) throws Throwable {
        List<Response.TransactionExtention> transactionsList = blockExtention.getTransactionsList();
        for (Response.TransactionExtention transactionExtention : transactionsList) {
            List<Chain.Transaction.Contract> contractList = transactionExtention.getTransaction().getRawData().getContractList();
            for (Chain.Transaction.Contract contract : contractList) {
                Class a = Class.forName("org.tron.trident.proto.Contract$" + contract.getType());
                Message unpack = contract.getParameter().unpack(a);
                // TriggerSmartContract是合约， 按照不同的交易类型检索
                if (unpack instanceof org.tron.trident.proto.Contract.TriggerSmartContract) {
                    // 合约地址
                    org.tron.trident.proto.Contract.TriggerSmartContract contractStr = (org.tron.trident.proto.Contract.TriggerSmartContract) unpack;
                    ByteString contractAddress = contractStr.getContractAddress();
                    //  String hex = ApiWrapper.toHex(contractAddress);
                    String contractAddressStr = Base58Check.bytesToBase58(contractAddress.toByteArray());
                    // 判断是否是自己需要监控的合约地址
                    if (trcProperty.getUsdtContarct().equalsIgnoreCase(contractAddressStr)) {
                        ByteString ownerAddress = contractStr.getOwnerAddress();
                        //  String fromAddress = Base58Check.bytesToBase58(ownerAddress.toByteArray());
                        ByteString data = contractStr.getData();
                        String hex1 = ApiWrapper.toHex(data);
                        // 交易类型是转账
                        String transferFunction = Hex.toHexString(new Keccak.Digest256().digest("transfer(address,uint256)".getBytes())).substring(0, 8);
                        String funcId = hex1.substring(0, 8);
                        if (!transferFunction.equals(funcId)) {
                            continue;
                        }
                        String toAddress = hex1.substring(32, 72);
                        String amount = hex1.substring(72, 136);
                        Address address = (Address) TypeDecoder.instantiateType("address", toAddress);
                        // 地址
                        NumericType amountType = (NumericType) TypeDecoder.instantiateType("uint256", amount);
                        // 金额
                        BigDecimal trc20Decimals = BigDecimal.valueOf(Math.pow(10, trcProperty.getDivisor()));
                        BigDecimal amountee = new BigDecimal(amountType.getValue()).divide(trc20Decimals, 6, RoundingMode.FLOOR);

                        byte[] byteArray = transactionExtention.getTransaction().getRawData().toByteArray();
                        byte[] bytes = Hash.sha256(byteArray);
                        String txId = Hex.toHexString(bytes);

                        // 后续处理
                        handlerRecharge(address.getValue(), amountee, txId);

                    }
                }
            }
        }
    }


    private Boolean handlerRecharge(String address, BigDecimal amount, String txId) {
        if (!ScanBlocksJob.TRC_ADDRESS.containsKey(address)) {
            //  log.info(address+"监听到地址到账【市场】");
            return false;
        }
        MemberRechargeAddress memberRechargeAddress = ScanBlocksJob.TRC_ADDRESS.get(address);
        //如果未有充值记录，则支付一笔TRX 进行激活账户
        if(memberRechargeAddress != null  && !memberRechargeAddress.getIsActivate()){
            //支付一笔
            String hash = transferTRX(address, 1.1F);
            memberRechargeAddressService.activateAddress(memberRechargeAddress.getMemberId(),memberRechargeAddress.getCoin(),amount);
            memberRechargeAddress.setIsActivate(true);
         //   ScanBlocksJob.TRC_ADDRESS.put(memberRechargeAddress.getAddress(),memberRechargeAddress);
        }

        log.info(address + "监听到地址到账" + amount);


        Boolean b = rechargeService.addRechare(memberRechargeAddress.getMemberId(), amount, address, txId);
        if (!b) {
            log.error(txId + "充值遇到错误，请手动处理");
        }

        return b;
    }

    public static long end = 0;

    public void scanBlock() throws Throwable {
        ApiWrapper wrapper = trcConfig.getApiWrapper();
        Response.BlockListExtention blockByLimitNext;
        Long endNum = wrapper.getNowBlock().getBlockHeader().getRawData().getNumber();
        long cha = 20;
        if(end +20 > endNum){
            cha = endNum - end;
        }

        end = endNum;

        blockByLimitNext = wrapper.getBlockByLimitNext(endNum - cha, endNum);
        // 区间块列表
        List<Response.BlockExtention> blockList = blockByLimitNext.getBlockList();
        for (Response.BlockExtention blockExtention : blockList) {
            switchDataTransaction(blockExtention);
        }
      //  wrapper.close();
    }





}
